using System;
using System.Diagnostics.Contracts;
using System.IO;

namespace OpenCube.IO
{
	[ContractClass(typeof(BinaryReaderContracts))]
	[CLSCompliant(false)]
	public interface IBinaryReader
	{
		/// <summary>
		/// Read a single byte from the stream.
		/// </summary>
		/// <returns>The byte read from the stream.</returns>
		/// <exception cref="EndOfStreamException">Thrown when there is less as 1 byte available on the stream</exception>
		byte ReadByte();

		/// <summary>
		/// Read a single signed byte from the stream.
		/// </summary>
		/// <returns>The signed byte read from the stream.</returns>
		/// <exception cref="EndOfStreamException">Thrown when there is less as 1 byte available on the stream</exception>
		sbyte ReadSByte();

		/// <summary>
		/// Read a signed 16 bit integer from the stream.
		/// </summary>
		/// <returns>The signed 16 bit integer read from the stream.</returns>
		/// <exception cref="EndOfStreamException">Thrown when there are less as 2 bytes available on the stream</exception>
		short ReadInt16();

		/// <summary>
		/// Read a unsigned 16 bit integer from the stream.
		/// </summary>
		/// <returns>The unsigned 16 bit integer read from the stream.</returns>
		/// <exception cref="EndOfStreamException">Thrown when there are less as 2 bytes available on the stream</exception>
		ushort ReadUInt16();

		/// <summary>
		/// Read a signed 24 bit integer from the stream.
		/// </summary>
		/// <returns>The signed 24 bit integer read from the stream.</returns>
		/// <exception cref="EndOfStreamException">Thrown when there are less as 3 bytes available on the stream</exception>
		int ReadInt24();

		/// <summary>
		/// Read a unsigned 24 bit integer from the stream.
		/// </summary>
		/// <returns>The unsigned 24 bit integer read from the stream.</returns>
		/// <exception cref="EndOfStreamException">Thrown when there are less as 3 bytes available on the stream</exception>
		uint ReadUInt24();

		/// <summary>
		/// Read a signed 32 bit integer from the stream.
		/// </summary>
		/// <returns>The signed 32 bit integer read from the stream.</returns>
		/// <exception cref="EndOfStreamException">Thrown when there are less as 4 bytes available on the stream</exception>
		int ReadInt32();

		/// <summary>
		/// Read a unsigned 32 bit integer from the stream.
		/// </summary>
		/// <returns>The unsigned 32 bit integer read from the stream.</returns>
		/// <exception cref="EndOfStreamException">Thrown when there are less as 4 bytes available on the stream</exception>
		uint ReadUInt32();

		/// <summary>
		/// Read a signed 64 bit integer from the stream.
		/// </summary>
		/// <returns>The signed 64 bit integer read from the stream.</returns>
		/// <exception cref="EndOfStreamException">Thrown when there are less as 8 bytes available on the stream</exception>
		long ReadInt64();

		/// <summary>
		/// Read a unsigned 64 bit integer from the stream.
		/// </summary>
		/// <returns>The unsigned 64 bit integer read from the stream.</returns>
		/// <exception cref="EndOfStreamException">Thrown when there are less as 8 bytes available on the stream</exception>
		ulong ReadUInt64();

		/// <summary>
		/// Read a single precision floating point number from the stream.
		/// </summary>
		/// <returns>The single precision floating point number read from the stream.</returns>
		/// <exception cref="EndOfStreamException">Thrown when there are less as 4 bytes available on the stream</exception>
		float ReadSingle();

		/// <summary>
		/// Read a double precision floating point number from the stream.
		/// </summary>
		/// <returns>The double precision floating point number read from the stream.</returns>
		/// <exception cref="EndOfStreamException">Thrown when there are less as 8 bytes available on the stream</exception>
		double ReadDouble();

		/// <summary>
		/// Read the given number of bytes from the stream
		/// </summary>
		/// <param name="count">The number of bytes to read from the stream.</param>
		/// <returns>An array with the number of bytes as read from the stream.</returns>
		/// <exception cref="EndOfStreamException">Thrown when there are less as the given number of bytes available on the stream</exception>
		byte[] ReadBytes(int count);

		/// <summary>
		/// Read the given number of signed bytes from the stream
		/// </summary>
		/// <param name="count">The number of signed bytes to read from the stream.</param>
		/// <returns>An array with the number of signed bytes as read from the stream.</returns>
		/// <exception cref="EndOfStreamException">Thrown when there are less as the given number of bytes available on the stream</exception>
		sbyte[] ReadSBytes(int count);
	}

	[ContractClassFor(typeof(IBinaryReader))]
	abstract class BinaryReaderContracts
		: IBinaryReader
	{
		public byte ReadByte()
		{
			throw new NotImplementedException();
		}

		public sbyte ReadSByte()
		{
			throw new NotImplementedException();
		}

		public short ReadInt16()
		{
			throw new NotImplementedException();
		}

		public ushort ReadUInt16()
		{
			throw new NotImplementedException();
		}

		public int ReadInt24()
		{
			Contract.Ensures(Contract.Result<int>() < 0x00800000);
			Contract.Ensures(Contract.Result<int>() >= -0x008000000);
			throw new NotImplementedException();
		}

		public uint ReadUInt24()
		{
			Contract.Ensures(Contract.Result<uint>() < 0x01000000);
			throw new NotImplementedException();
		}

		public int ReadInt32()
		{
			throw new NotImplementedException();
		}

		public uint ReadUInt32()
		{
			throw new NotImplementedException();
		}

		public long ReadInt64()
		{
			throw new NotImplementedException();
		}

		public ulong ReadUInt64()
		{
			throw new NotImplementedException();
		}

		public float ReadSingle()
		{
			throw new NotImplementedException();
		}

		public double ReadDouble()
		{
			throw new NotImplementedException();
		}

		public byte[] ReadBytes(int count)
		{
			Contract.Requires<ArgumentOutOfRangeException>(count >= 1, "The number of bytes to read should be at least 1");
			Contract.Ensures(Contract.Result<byte[]>() != null);
			Contract.Ensures(Contract.Result<byte[]>().Length == count);
			throw new NotImplementedException();
		}

		public sbyte[] ReadSBytes(int count)
		{
			Contract.Requires<ArgumentOutOfRangeException>(count >= 1, "The number of bytes to read should be at least 1");
			Contract.Ensures(Contract.Result<sbyte[]>() != null);
			Contract.Ensures(Contract.Result<sbyte[]>().Length == count);
			throw new NotImplementedException();
		}
	}
}